package com.upb.webserver.rest.fisco;

import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.upb.webserver.common.enums.PlatformEnum;
import com.upb.webserver.common.enums.RoleTypeEnum;
import com.upb.webserver.common.exception.WebServerException;
import com.upb.webserver.common.pojo.account.AccountCommon;
import com.upb.webserver.common.pojo.overview.RespSysOverview;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.resp.RespNode;
import com.upb.webserver.common.returncode.ConstantCode;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.upb.webserver.common.constant.RestConstant;
import com.upb.webserver.common.pojo.base.BasePageResponse;
import com.upb.webserver.common.pojo.base.BaseResponse;
import com.upb.webserver.common.pojo.rest.fisco.data.req.EventExportInfo;
import com.upb.webserver.common.pojo.rest.fisco.data.req.EventExportUpdateParam;
import com.upb.webserver.common.pojo.rest.fisco.data.req.EventInfoParam;
import com.upb.webserver.common.pojo.rest.fisco.data.req.NormalSearchParamForFisco;
import com.upb.webserver.common.pojo.rest.fisco.data.resp.ChainInfo;
import com.upb.webserver.common.properties.RestProperties;
import com.upb.webserver.common.tools.HttpRequestTools;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.rest.AbstractRestService;

/**
 * webase-data-fetcher interface service
 */
@Service
public class FiscoDataFetchRestService extends AbstractRestService {

    @Autowired
    private RestProperties restProperties;
    private String serverName = RestConstant.SERVER_NAME_WEBASE_DATA_FETCHER;
    private String serverNameChain = RestConstant.SERVER_NAME_WEBASE_CHAIN_MANAGER;

    @PostConstruct
    public void init() {
        initServerAddressMap(serverName, restProperties.getWebaseDataFetcherAddressList());
    }

    /**
     * get data overview.
     */
    public BaseResponse getDataGeneral() {
        BaseResponse restRsp = httpGet(RestConstant.URI_WEBASE_DATA_FETCHER_CHAIN_GENERAL, BaseResponse.class);
        return restRsp;
    }

    /**
     * get chain list.
     */
    public BasePageResponse getChainList() {
        BasePageResponse restRsp = httpGet(RestConstant.URI_WEBASE_DATA_FETCHER_CHAIN_ALL, BasePageResponse.class);
        return restRsp;
    }

    public Pair<Integer, List<ChainInfo>> getChainListPair() {
        List<ChainInfo> list = new ArrayList<>();
        try {
            BasePageResponse restRsp = httpGet(RestConstant.URI_WEBASE_DATA_FETCHER_CHAIN_ALL, BasePageResponse.class);
            list = JsonUtils.stringToObj(JsonUtils.objToString(restRsp.getData()),
                    new TypeReference<List<ChainInfo>>() {
                    });
            return Pair.of((int) restRsp.getTotalCount(), list);
        } catch (Exception e) {
            return Pair.of(0, list);
        }
    }

    /**
     * get group list by chainId.
     */
    public BasePageResponse getGroupList(Integer chainId) {
        Map<String, String> map = new HashMap<>();
        if (chainId != null) {
            map.put("chainId", String.valueOf(chainId));
        }

        String uri = HttpRequestTools.getQueryUri(RestConstant.URI_WEBASE_DATA_FETCHER_GROUP_LIST, map);
        BasePageResponse restRsp = httpGet(uri, BasePageResponse.class);
        return restRsp;
    }

    /**
     * get group info.
     */
    public BaseResponse getGroupGeneral(Integer chainId, Integer groupId) {
        String uri = String.format(RestConstant.URI_WEBASE_DATAGROUP_GENERAL, chainId, groupId);
        BaseResponse restRsp = httpGetChain(uri, BaseResponse.class);
        return restRsp;
    }

    /**
     * get transaction daily data.
     */
    public BaseResponse getTransDaily(Integer chainId, Integer groupId) {
        String uri = String.format(RestConstant.URI_WEBASE_DATAGROUP_TXNDAILY, chainId, groupId);
        BaseResponse restRsp = httpGetChain(uri, BaseResponse.class);
        return restRsp;
    }

    /**
     * get node list.
     */
    public BasePageResponse getNodeList(Integer chainId, Integer groupId, Integer pageNumber, Integer pageSize) {
        String uri = String.format(RestConstant.URI_WEBASE_DATAGROUP_NODELIST, chainId, groupId, pageNumber,
                pageSize);
        BasePageResponse restRsp = httpGetChain(uri, BasePageResponse.class);
        JSONArray jsonArray = new JSONArray(restRsp.getData());
        JSONArray newJsonArray = new JSONArray();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject object = jsonArray.getJSONObject(i);
            object.set("createTime", formatter.format(object.get("createTime")));
            object.set("modifyTime", formatter.format(object.get("modifyTime")));
            newJsonArray.add(object);
        }
        restRsp.setData(newJsonArray);
        return restRsp;
    }

    /**
     * get block list.
     */
    public BasePageResponse getBlockList(Integer chainId, Integer groupId, Integer pageNumber, Integer pageSize,
                                         String blockHash, BigInteger blockNumber) {
        Map<String, String> map = new HashMap<>();
        if (blockHash != null) {
            map.put("blockHash", String.valueOf(blockHash));
        }
        if (blockNumber != null) {
            map.put("blockNumber", String.valueOf(blockNumber));
        }

        String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATAGROUP_BLOCKLIST,
                chainId, groupId, pageNumber, pageSize), map);
        BasePageResponse restRsp = httpGetChain(uri, BasePageResponse.class);
        return restRsp;
    }

    /**
     * get transaction list.
     */
    public BasePageResponse getTransList(Integer chainId, Integer groupId, Integer pageNumber, Integer pageSize,
                                         String transHash, BigInteger blockNumber, AccountCommon accountCommon) {

        boolean shouldReqToWebaseData = false;
        if (accountCommon.getPlatform() == PlatformEnum.AUDIT.getId()) {
            shouldReqToWebaseData = true;//if request comes from audit, req to webase-data
        }

        Map<String, String> map = new HashMap<>();
        if (transHash != null) {
            map.put("transHash", String.valueOf(transHash));
        }
        if (blockNumber != null) {
            map.put("blockNumber", String.valueOf(blockNumber));
        }

        BasePageResponse restRsp = null;
        if (shouldReqToWebaseData) {
            String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATA_FETCHER_GROUP_TRANSLIST,
                    chainId, groupId, pageNumber, pageSize), map);
            restRsp = httpGet(uri, BasePageResponse.class);
        } else {
            String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATAGROUP_TRANSLIST,
                    chainId, groupId, pageNumber, pageSize), map);
            restRsp = httpGetChain(uri, BasePageResponse.class);
        }
        return restRsp;
    }

    /**
     * get user list.
     */
    public BasePageResponse getUserList(Integer chainId, Integer groupId, Integer pageNumber, Integer pageSize,
                                        String userParam) {
        Map<String, String> map = new HashMap<>();
        if (userParam != null) {
            map.put("userParam", String.valueOf(userParam));
        }

        String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATA_FETCHER_GROUP_USERLIST,
                chainId, groupId, pageNumber, pageSize), map);
        BasePageResponse restRsp = httpGet(uri, BasePageResponse.class);
        return restRsp;
    }

    /**
     * get contract list.
     */
    public BasePageResponse getContractList(Integer chainId, Integer groupId, Integer pageNumber, Integer pageSize,
                                            String contractParam) {
        Map<String, String> map = new HashMap<>();
        if (contractParam != null) {
            map.put("contractParam", String.valueOf(contractParam));
        }

        String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATA_FETCHER_GROUP_CONTRACTLIST,
                chainId, groupId, pageNumber, pageSize), map);
        BasePageResponse restRsp = httpGet(uri, BasePageResponse.class);
        return restRsp;
    }

    /**
     * search data by condition.
     */
    public BasePageResponse searchMormalList(NormalSearchParamForFisco queryParam, AccountCommon accountCommon) {

        boolean shouldReqToWebaseData = false;
        if (accountCommon.getPlatform() == PlatformEnum.AUDIT.getId()) {
            shouldReqToWebaseData = true;//if request comes from audit, req to webase-data
        }

        BasePageResponse restRsp;
        if (shouldReqToWebaseData) {
            try {
                restRsp = httpPost(RestConstant.URI_WEBASE_DATA_FETCHER_SEARCH_NORMAL, queryParam,
                        BasePageResponse.class);
            } catch (Exception e) {
                throw new WebServerException(ConstantCode.DATA_SERVER_REQUEST_FAIL);
            }
        } else {
            String uri = String.format(RestConstant.URI_WEBASE_DATAGROUP_TRANSINFO,
                    queryParam.getChainId(), queryParam.getGroupId(), queryParam.getTransHash());
            restRsp = httpGetChain(uri, BasePageResponse.class);
        }
        JSONObject object = new JSONObject(restRsp.getData());
        restRsp.setData(object.toString());
        return restRsp;
    }


    public BasePageResponse searchReceiptList(NormalSearchParamForFisco queryParam) {
        BaseResponse baseResponse = httpGetChain(String.format(RestConstant.URI_WEBASE_NODE_ALL), BaseResponse.class);
        List<RespNode> nodeList = JsonUtils.stringToObj(JsonUtils.objToString(baseResponse.getData()),
        new TypeReference<List<RespNode>>() {
        });
        BasePageResponse restRsp = null;
        for(RespNode node : nodeList){
            if(node.getChainId().equals(queryParam.getChainId()) && node.getGroupId().equals(queryParam.getGroupId())){
                String uri = String.format(RestConstant.URI_WEBASE_NODE_RECEIPT,
                        queryParam.getChainId(), queryParam.getGroupId(), node.getNodeId(), queryParam.getTransHash());
                restRsp = httpGetChain(uri, BasePageResponse.class);
            }
        }
        return restRsp;
    }

    /**
     * search data by condition.
     */
    @Deprecated
    public BasePageResponse searchMormalList(NormalSearchParamForFisco queryParam) {
        BasePageResponse restRsp;
        if (StringUtils.isNoneBlank(queryParam.getBlockParam())) {
            try {
                restRsp = httpPost(RestConstant.URI_WEBASE_DATA_FETCHER_SEARCH_NORMAL, queryParam,
                        BasePageResponse.class);
            } catch (Exception e) {
                throw new WebServerException(ConstantCode.DATA_SERVER_REQUEST_FAIL);
            }
        } else {
            String uri = String.format(RestConstant.URI_WEBASE_DATAGROUP_TRANSINFO,
                    queryParam.getChainId(), queryParam.getGroupId(), queryParam.getTransHash());
            restRsp = httpGetChain(uri, BasePageResponse.class);
        }
        return restRsp;
    }

    /**
     * search data by keyword.
     */
    public BasePageResponse searchByKeyword(Integer pageNumber, Integer pageSize, String keyword) {
        String uri = String.format(RestConstant.URI_WEBASE_DATA_FETCHER_SEARCH_KEYWORD, pageNumber, pageSize, keyword);
        BasePageResponse restRsp = httpGet(uri, BasePageResponse.class);
        return restRsp;
    }

    /**
     * add event export info.
     */
    public BaseResponse newEventExportInfo(EventExportInfo auditInfo) {
        BaseResponse restRsp = httpPost(RestConstant.URI_WEBASE_DATA_FETCHER_EVENT_EXPORT_ADD, auditInfo,
                BaseResponse.class);
        return restRsp;
    }

    /**
     * get event export info list.
     */
    public BasePageResponse getEventExportInfoList(Integer pageNumber, Integer pageSize, Integer chainId,
                                                   Integer groupId, String contractAddress) {
        Map<String, String> map = new HashMap<>();
        if (contractAddress != null) {
            map.put("contractAddress", String.valueOf(contractAddress));
        }

        String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATA_FETCHER_EVENT_EXPORT_LIST,
                chainId, groupId, pageNumber, pageSize), map);
        BasePageResponse restRsp = httpGet(uri, BasePageResponse.class);
        return restRsp;
    }

    /**
     * update event export info.
     */
    public BaseResponse updateExportStatus(EventExportUpdateParam param) {
        BaseResponse restRsp = httpPost(RestConstant.URI_WEBASE_DATA_FETCHER_EVENT_EXPORT_UPDATE, param, BaseResponse.class);
        return restRsp;
    }

    /**
     * delete event export info by id.
     */
    public BaseResponse removeEventExportInfo(Integer id) {
        String uri = String.format(RestConstant.URI_WEBASE_DATA_FETCHER_EVENT_EXPORT_DELETE, id);
        BaseResponse restRsp = httpGet(uri, BaseResponse.class);
        return restRsp;
    }

    /**
     * get event info list.
     */
    public BasePageResponse getEvenInfoList(EventInfoParam eventInfoParam) {
        BasePageResponse restRsp = httpPost(RestConstant.URI_WEBASE_DATA_FETCHER_EVENT_INFO_LIST, eventInfoParam,
                BasePageResponse.class);
        return restRsp;
    }


    @Override
    public <T> T httpGet(String uri, Class<T> clazz) {
        return super.restTemplateExchange(serverName, RestConstant.FISCO_DATA_FETCHER_URL_FORMAT, uri, HttpMethod.GET,
                null, clazz);
    }

    @Override
    public <T> T httpPost(String uri, Object param, Class<T> clazz) {
        return super.restTemplateExchange(serverName, RestConstant.FISCO_DATA_FETCHER_URL_FORMAT, uri, HttpMethod.POST,
                param, clazz);
    }

    public <T> T httpGetChain(String uri, Class<T> clazz) {
        return super.restTemplateExchange(serverNameChain, RestConstant.FISCO_CHAIN_MANAGER_URL_FORMAT, uri, HttpMethod.GET,
                null, clazz);
    }

    @Override
    public <T> T httpPut(String uri, Object param, Class<T> clazz) {
        return super.restTemplateExchange(serverName, RestConstant.FISCO_DATA_FETCHER_URL_FORMAT, uri, HttpMethod.PUT,
                param, clazz);
    }

    @Override
    public <T> T httpDelete(String uri, Object param, Class<T> clazz) {
        return super.restTemplateExchange(serverName, RestConstant.FISCO_DATA_FETCHER_URL_FORMAT, uri,
                HttpMethod.DELETE, param, clazz);
    }

    /**
     * system overview part
     */

    public BaseResponse getTranxAllCount(RespSysOverview respSysOverview) {
        String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATA_FETCHER_OVERVIEW_TRANSCOUNTALL,
                respSysOverview.getChainId(), respSysOverview.getGroupId(), respSysOverview.getStartTime(), respSysOverview.getEndTime()), new HashMap<>());
        BaseResponse restRsp = httpGet(uri, BaseResponse.class);
        JSONObject jsonObject = new JSONObject(restRsp.getData());
        JSONObject newJsonObject = new JSONObject();
        newJsonObject.set("totalCount", jsonObject.get("tranxCount"));
        newJsonObject.set("commitCount", jsonObject.get("tranxCount"));
        restRsp.setData(newJsonObject);
        return restRsp;
    }

    public BaseResponse getBlockAllCount(RespSysOverview respSysOverview) {
        String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATA_FETCHER_OVERVIEW_BLOCKCOUNTALL,
                respSysOverview.getChainId(), respSysOverview.getGroupId(), respSysOverview.getStartTime(), respSysOverview.getEndTime()), new HashMap<>());
        BaseResponse restRsp = httpGet(uri, BaseResponse.class);
        JSONObject jsonObject = new JSONObject(restRsp.getData());
        JSONObject newJsonObject = new JSONObject();
        newJsonObject.set("totalCount", jsonObject.get("blockCount"));
        newJsonObject.set("commitCount", jsonObject.get("blockCount"));
        restRsp.setData(newJsonObject);
        return restRsp;
    }


    public BasePageResponse getTranxAllList(RespSysOverview respSysOverview) {
        String uri = HttpRequestTools.getQueryUri(String.format(RestConstant.URI_WEBASE_DATA_FETCHER_OVERVIEW_TRANSLISTALL,
                respSysOverview.getChainId(), respSysOverview.getGroupId(), respSysOverview.getStartTime(), respSysOverview.getEndTime()), new HashMap<>());
        BasePageResponse restRsp = httpGet(uri, BasePageResponse.class);
        return restRsp;
    }
}
